﻿// -------------------------------------------------------------------------
//    @FileName         :    NFPlayerUtil.cpp
//    @Author           :    gaoyi
//    @Date             :    24-8-13
//    @Email            :    445267987@qq.com
//    @Module           :    NFPlayerUtil
//
// -------------------------------------------------------------------------

#include "NFPlayerUtil.h"

#include <E_PlayerLevelUp.nanopb.h>
#include <NFOnlineUtil.h>
#include <NFPacketSendUtil.h>
#include <NFSessionModule.h>
#include <NFEventDisp.h>
#include <NFPlayerPacketSendUtil.h>
#include <NFServerLevelMgr.h>
#include <NFSessionUtil.h>
#include <DescStore/PlayerLevelUpDesc.h>
#include <NFLogicCommon/NFCommMacros.h>

#include "NFHeroPart.h"
#include "NFOnlineService.h"
#include "NFPackageUtil.h"
#include "NFPlayer.h"
#include "NFPlayerMgr.h"
#include "NFServiceHero.h"
#include "NFSession.h"
#include "proto_db.pb.h"
#include "proto_svr_common.pb.h"
#include "proto_svr_sns.pb.h"

#define MAX_TICK_PLAYER_NUM_PER_Tick 100

int NFPlayerUtil::LoginPlayer(NFPlayer* pPlayer, bool iIsResume)
{
    LOGPLAYER_INFO(pPlayer->GetRoleID(), "RoleID:" << pPlayer->GetRoleID() << " Login Success!Resume=" << iIsResume << ",Previous Status=" << pPlayer->GetPlayerStatus());
    if (iIsResume)
    {
        pPlayer->OnReconnect();
        NFOnlineService::SendSSSyncOnlineReq(pPlayer);
    }
    else
    {
        NFOnlineService::SendSSRegOnlineReq(pPlayer);
    }

    pPlayer->OnLogin();
    pPlayer->DayTick();
    pPlayer->DayOtherTick();
    pPlayer->WeekTick();
    pPlayer->MonthTick();
    NFEventDisp::OnPlayerLogin(pPlayer, iIsResume);

    NFSession* pSession = pPlayer->GetClientSession();
    CHECK_NULL(0, pSession);

    pPlayer->SetPlatType(pSession->GetClientInfo()->plat_type);

    return 0;
}

int NFPlayerUtil::LogoutPlayer(NFPlayer* pPlayer)
{
    LOGSVR_INFO("RoleID:" << pPlayer->GetRoleID() << " Logout Success!Status=" << pPlayer->GetPlayerStatus());

    if (pPlayer->GetPlayerStatus() != CACHE_STATUS_NONE)
    {
        if (pPlayer->IsNeedSave())
        {
            LOGSVR_ERROR("RoleID:" << pPlayer->GetRoleID() << " can't logout because not saved.");
            return -1;
        }

        //player logout
        pPlayer->OnLogout();
        NFEventDisp::OnPlayerLogout(pPlayer);
    }

    if (pPlayer->GetClientSession())
    {
        pPlayer->GetClientSession()->SetWillDelete();
    }

    NFOnlineService::SendSSExitOnlineReq(pPlayer);
    pPlayer->SetPlayerStatus(CACHE_STATUS_DELETE);
    NFLogInfo(NF_LOG_DEFAULT, pPlayer->StatusId(), "StatusId:{} status change to PLAYER_STATUS_DEAD, will be erase from memory", pPlayer->StatusId());

    return 0;
}

int NFPlayerUtil::DisconnectPlayer(NFPlayer* pPlayer, int iType, int iReason)
{
    CHECK_NULL(0, pPlayer);

    pPlayer->OnDisconnect();
    NFEventDisp::OnPlayerDisconnect(pPlayer, iType, iReason);
    return 0;
}

int NFPlayerUtil::OnPlayerLogin(NFPlayer* pPlayer, bool iIsResume)
{
    CHECK_NULL(0, pPlayer);
    if (!iIsResume)
    {
        pPlayer->ResetCsMsgSeq();
        NFServerConfig* pConfig = NFGlobalSystem::Instance()->GetGlobalPluginManager()->FindModule<NFIConfigModule>()->GetAppConfig(NF_ST_LOGIC_SERVER);
        NF_ASSERT(pConfig);

        proto_ff::Proto_GSLoginReq loginReq;
        loginReq.set_role_id(pPlayer->GetRoleID());
        loginReq.set_gamesvr_id(pConfig->GetBusId());
        pPlayer->SaveRoleSimpleInfo(loginReq.mutable_role_info());
        loginReq.mutable_role_info()->clear_team_count();
        loginReq.mutable_role_info()->clear_team_max_count();
        loginReq.mutable_role_info()->clear_team_id();

        loginReq.set_last_login_time(pPlayer->GetLastLoginTime());
        loginReq.set_last_logout_time(pPlayer->GetLastLogoutTime());

        pPlayer->SendMsgToSnsServer(proto_ff::E_GS_LOGIN_REQ, loginReq);

        LOG_INFO(pPlayer->GetRoleID(), "Role Send Sns Login....");
    }
    return 0;
}

int NFPlayerUtil::OnPlayerLogout(NFPlayer* pPlayer)
{
    CHECK_NULL(0, pPlayer);
    NFServerConfig* pConfig = NFGlobalSystem::Instance()->GetGlobalPluginManager()->FindModule<NFIConfigModule>()->GetAppConfig(NF_ST_LOGIC_SERVER);
    NF_ASSERT(pConfig);

    proto_ff::Proto_GSLogoutReq req;
    req.set_role_id(pPlayer->GetRoleID());
    req.set_gamesvr_id(pConfig->GetBusId());
    pPlayer->SendMsgToSnsServer(proto_ff::E_GS_LOGOUT_REQ, req);

    return 0;
}

int NFPlayerUtil::OnPlayerDisconnect(NFPlayer* pPlayer, int iType, int iReason)
{
    CHECK_NULL(0, pPlayer);
    return 0;
}

int NFPlayerUtil::DeletePlayer(NFPlayer* pPlayer)
{
    int iRetCode = 0;

    CHECK_NULL(0, pPlayer);

    LOGSVR_INFO("DeletePlayer RoleID:" << pPlayer->GetRoleID() << ",GlobalID:" << pPlayer->GetGlobalId());

    NFEventDisp::OnDestroyPlayer(pPlayer);

    //pPlayer->Fini();

    iRetCode = NFPlayerMgr::Instance()->DeletePlayer(pPlayer);
    CHECK_RET(iRetCode, "DeletePlayer Player Failed");

    return 0;
}

uint64_t NFPlayerUtil::CalcAccountUIN(proto_ff::Proto_PlatType ePlatType, proto_ff::eAccountType eAccoutType, const std::string& pszOpenID)
{
    proto_ff::AccountInfo accInfo;
    std::string accountStream;

    accInfo.set_account_id(pszOpenID);
    accInfo.set_account_type(eAccoutType);
    accInfo.set_plat_type(ePlatType);
    accInfo.SerializePartialToString(&accountStream);
    uint64_t ullUin = NFSessionModule::BKDRHash(accountStream.c_str(), accountStream.size());

    return ullUin;
}

int NFPlayerUtil::KickPlayer(NFPlayer* pPlayer, proto_ff::Proto_CS_ErrorCode eErrCode, const char* sReason)
{
    CHECK_NULL(0, pPlayer);

    NFOnlineUtil::KickOff(pPlayer->GetRoleID(), eErrCode, sReason);

    return 0;
}

NFPlayer* NFPlayerUtil::GetPlayer(int iGlobalID, bool bPrintTraceStack, TagGetPlayerType enType)
{
    NFPlayer* pPlayer = NFPlayer::GetObjByGlobalId(iGlobalID);
    if (pPlayer)
    {
        switch (enType)
        {
            case GET_PLAYER_ANY:
            {
                return pPlayer;
            }
            default:
            {
                if (pPlayer->GetPlayerStatus() != CACHE_STATUS_ONLINE)
                {
                    if (bPrintTraceStack)
                    {
                        LOG_WARN(pPlayer->GetRoleID(), -1, "cur status {}, not online, fix to null, callstack {}", pPlayer->GetPlayerStatus(), TRACE_STACK());
                    }
                    else
                    {
                        LOG_INFO(pPlayer->GetRoleID(), "cur status {}, not online, fix to null", pPlayer->GetPlayerStatus());
                    }

                    pPlayer = NULL;
                }
            }
        }
    }

    return pPlayer;
}

NFPlayer* NFPlayerUtil::GetActivePlayer(int iGlobalID, bool bPrintTraceStack)
{
    NFPlayer* pPlayer = GetPlayer(iGlobalID);

    if (pPlayer && pPlayer->GetPlayerStatus() != CACHE_STATUS_ONLINE)
    {
        if (bPrintTraceStack)
        {
            LOG_WARN(pPlayer->GetRoleID(), -1, "cur status {}, not online, fix to null, callstack {}", pPlayer->GetPlayerStatus(), TRACE_STACK());
        }
        else
        {
            LOG_INFO(pPlayer->GetRoleID(), "cur status {}, not online, fix to null, callstack {}", pPlayer->GetPlayerStatus(), TRACE_STACK());
        }
    }

    return pPlayer;
}

NFPlayer* NFPlayerUtil::GetPlayerByRoleID(uint64_t ullRoleID)
{
    return NFPlayer::GetObjByHashKey(ullRoleID);
}

NFPlayer* NFPlayerUtil::GetActivePlayerByRoleID(uint64_t ullRoleID)
{
    auto pPlayer = GetPlayerByRoleID(ullRoleID);
    if (pPlayer && pPlayer->GetPlayerStatus() != CACHE_STATUS_ONLINE && pPlayer->GetPlayerStatus() != CACHE_STATUS_OFFLINE)
    {
        return NULL;
    }

    return pPlayer;
}

int NFPlayerUtil::OnPlayerRestore(NFPlayer* pPlayer)
{
    NFSession* pSession = pPlayer->GetClientSession();
    if (pSession)
    {
    }
    return 0;
}

int NFPlayerUtil::OnPlayerRestored(NFPlayer* pPlayer, NFSession* pOld, NFSession* pNew)
{
    CHECK_NULL(0, pPlayer);
    CHECK_NULL(0, pOld);
    CHECK_NULL(0, pNew);

    if (pOld->GetClientInfo()->plat_type == pNew->GetClientInfo()->plat_type)
    {
        return 0;
    }


    return 0;
}

int NFPlayerUtil::RestorePlayer(NFPlayer* pPlayer, NFSession* pSession)
{
    CHECK_NULL(0, pPlayer);
    CHECK_NULL(0, pSession);

    NFSession* pOldSession = pPlayer->GetClientSession();
    if (pOldSession && pOldSession->GetGlobalId() != pSession->GetGlobalId())
    {
        int iDelayStopMs = 0;
        switch (pPlayer->GetPlayerStatus())
        {
            case CACHE_STATUS_NONE:
            {
                pOldSession->SendConndStop();
                break;
            }
            case CACHE_STATUS_ONLINE:
            {
                NFPlayerPacketSendUtil::NotifyKickOff(pPlayer, proto_ff::ERR_CODE_ROLE_KICKOFF_HEARTBEAT_TIMEOUT);
                iDelayStopMs = 2000;
                LOGSVR_DEBUG("restore player, killoff pre");
                break;
            }
            default:
                break;
        }

        pSession->CloneSessionSeq(pOldSession);
        pOldSession->BindPlayer(nullptr);
        NFSessionUtil::DestorySessionObj(pOldSession, iDelayStopMs);
        OnPlayerRestored(pPlayer, pOldSession, pSession);
    }

    pPlayer->SetPlayerStatus(CACHE_STATUS_NONE);
    NFEventDisp::OnPlayerRestored(pPlayer);

    return 0;
}

int NFPlayerUtil::MakeRoleDetailForNew(proto_ff::tbRoleDetail* pRoleDetailOut, uint64_t ullRoleID, RoleOne& stRoleOne)
{
    pRoleDetailOut->set_role_id(ullRoleID);

    return 0;
}

bool NFPlayerUtil::IsExpFull(NFPlayer* pPlayer)
{
    auto pLevelUpDesc = PlayerLevelUpDesc::Instance()->GetDesc(pPlayer->GetLevel() + 1);
    if (!pLevelUpDesc)
    {
        return true;
    }

    return false;
}

int NFPlayerUtil::AddExp(NFPlayer* pPlayer, uint64_t ullAddExp, int eReason, int iReasonParam, uint64_t* ullRealAdd)
{
    CHECK_NULL(0, pPlayer);

    CHECK_EXPR(ullAddExp > 0, -1, "invalid addexp:{}", ullAddExp);

    int iOldLevel = pPlayer->GetLevel();

    double fExpRate = 0.3f;
    NFServerLevelMgr::Instance()->GetPlayerExpRate(iOldLevel, fExpRate);
    ullAddExp *= fExpRate;

    uint64_t iOldExp = pPlayer->GetExp();
    uint64_t iExp = pPlayer->GetExp() + ullAddExp;
    uint64_t iRealAddExp = ullAddExp;

    if (IsExpFull(pPlayer))
    {
        if (ullRealAdd)
            *ullRealAdd = 0;
        return ExpToMoney(pPlayer, ullAddExp, eReason);
    }

    int iServerLevel = NFServerLevelMgr::Instance()->GetServerLevel();
    if (iOldLevel >= iServerLevel)
    {
        if (ullRealAdd)
            *ullRealAdd = 0;
        return ExpToMoney(pPlayer, ullAddExp, eReason);
    }

    int iNewLevel = iOldLevel;
    E_PlayerLevelUp* pLevelUpDesc = nullptr;
    for (int iLevel = iOldLevel + 1; iLevel <= MAX_PLAYER_LEVEL; ++iLevel)
    {
        pLevelUpDesc = PlayerLevelUpDesc::Instance()->GetDesc(iLevel);
        if (!pLevelUpDesc) //exp full
        {
            break;
        }

        if (iExp < (uint64_t)pLevelUpDesc->exp)
        {
            break;
        }
        else
        {
            iExp -= pLevelUpDesc->exp;
            iNewLevel++;

            if (iNewLevel >= iServerLevel)
                break;
        }
    }

    pPlayer->SetLevel(iNewLevel);
    if (iNewLevel >= iServerLevel || !pLevelUpDesc)
    {
        iRealAddExp -= iExp;
        ExpToMoney(pPlayer, iExp, (int)eReason);
    }
    else
    {
        pPlayer->SetExp(iExp);
    }

    pPlayer->MarkGSExpModify();
    if (ullRealAdd != nullptr)
    {
        *ullRealAdd = iRealAddExp;
    }
    NotifyAttr(pPlayer, true);

    //
    NFServiceHero::SetAttr(pPlayer, proto_ff::EN_ATTR_LEVEL, iNewLevel, eReason, iReasonParam);

    LOG_DEBUG(pPlayer->GetRoleID(), "OldLevel:{}, OldExp:{}, NewLevel:{}, NewExp:{}, AddExp:{}, RealAddExp:{}", iOldLevel, iOldExp, pPlayer->GetLevel(), pPlayer->GetExp(), ullAddExp, iRealAddExp);

    NFEventDisp::OnAddExp(pPlayer, iRealAddExp, iOldLevel, iOldExp, eReason, iReasonParam);

    if (iOldLevel < iNewLevel)
    {
        NFEventDisp::OnPlayerLevelUp(pPlayer, iOldLevel);
        NFServiceHero::RecalcHeroAttr(pPlayer, eReason);
    }

    return 0;
}

int NFPlayerUtil::ExpToMoney(NFPlayer* pPlayer, uint64_t uExp, int eReason)
{
    return 0;
}

double NFPlayerUtil::CalcExpBonus(NFPlayer* pPlayer, double exp)
{
    if (pPlayer == nullptr)
        return 0;
    if (exp == 0)
        return 0;

    auto pHeroPart = pPlayer->GetPart<NFHeroPart>();
    if (pHeroPart == nullptr)
        return 0;

    return exp * pHeroPart->GetAttr(proto_ff::EN_ATTR_BONUS_EXP);
}

int NFPlayerUtil::NotifyAttr(NFPlayer* pPlayer, bool bAll)
{
    CHECK_NULL(0, pPlayer);

    LOGPLAYER_TRACE(pPlayer->GetRoleID(), "NotifyAttr bAll=" << bAll);

    proto_ff::Proto_CSRsp rsp;
    proto_ff::Proto_CSRoleAttrList* pRoleAttrList = rsp.mutable_role_attr_ntf();

    int iNum = pPlayer->MakeAttrCS(pRoleAttrList, bAll);
    if (iNum <= 0)
    {
        LOGPLAYER_DEBUG(pPlayer->GetRoleID(), "MakeAttrCS ret num=" << iNum << ", do not need notify");
        return 0;
    }

    int iRetCode = NFPacketSendUtil::SendToClient(pPlayer, proto_ff::E_CS_ROLE_ATTR_NTF, &rsp);
    CHECK_PLAYER_RET(pPlayer->GetRoleID(), iRetCode, "CGameSvr2CliPacketSender::SendToClient Failed Info:{}", pPlayer->GetRoleID());

    pPlayer->ClearAttrChangeFlag();

    return iRetCode;
}

int NFPlayerUtil::Tick()
{
    static auto s_lastIter = NFPlayer::Begin();
    for (int i = 0; i < MAX_TICK_PLAYER_NUM_PER_Tick; i++)
    {
        if (!NFPlayer::IsValid(s_lastIter) || s_lastIter == NFPlayer::End())
        {
            s_lastIter = NFPlayer::Begin();
            if (s_lastIter == NFPlayer::End())
            {
                break;
            }
            if (i > NFPlayer::GetStaticUsedCount())
            {
                break;
            }
        }

        auto pPlayer = &*s_lastIter;
        CHECK_NULL(0, pPlayer);
        pPlayer->Tick();
        if (pPlayer->GetPlayerStatus() == CACHE_STATUS_DELETE)
        {
            s_lastIter = NFPlayer::Erase(s_lastIter);
        }
        else
        {
            ++s_lastIter;
        }
    }

    return 0;
}